package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;
import ptolemy.math.Complex;

import ptolemy.domains.de.kernel.*;

//////////////////////////////////////////////////////////////////////////
//// DFT
/**

@author Ivan Jeukens
*/
public class DFT extends DEActor {    

    public DFT(CompositeEntity container, String name) throws 
            NameDuplicationException, IllegalActionException {
        super(container, name);
        
        inputMode = new Parameter(this, "inputMode", new IntToken(1));
        _inputMode = 1;
        outputMode = new Parameter(this, "outputMode", new IntToken(1));
        _outputMode = 1;

        mode = new DEIOPort(this, "mode", true, false);
	mode.setTypeEquals(BaseType.BOOLEAN);

        _createInputInterface();
        _createOutputInterface();        
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public DEIOPort input;
    public DEIOPort mode;        
    public DEIOPort subChannel[];

    public Parameter inputMode;
    public Parameter outputMode;    
    
    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == inputMode) {
	/*    IntToken it = (IntToken) inputMode.getToken();
	    _inputMode = it.intValue();
            try {
                _createInputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */
        }
        else
        if(attribute == outputMode) {
            /*IntToken it = (IntToken) outputMode.getToken();
            _outputMode = it.intValue();
            try {
                _createOutputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */           
        }       
	else {
            super.attributeChanged(attribute);
        }        
    }

    public void initialize() throws IllegalActionException {
        super.initialize();	
	_data = new double[128];
	_mode = false;
    }
    
    public void fire() throws IllegalActionException {        	
        int index;
	
        if(mode.hasToken(0)) {
	    BooleanToken bt = (BooleanToken) mode.get(0);
	    _mode = bt.booleanValue();
	}
	else {
	    if(_inputMode == 0) {
	        for(int i = 0;i < 128;i += 2) {
	            DoubleToken dt = (DoubleToken) input.get(0);
	            _data[i] = dt.doubleValue();
	            _data[i+1] = 0;
	        }
	    }
	    else {
	        ObjectToken ot = (ObjectToken) input.get(0);
		double [] tmp = (double []) ot.getValue();
		for(int i = 0;i < 64;i++) {
		    _data[2*i] = tmp[i];
		    _data[2*i + 1] = 0;
		}
	    }
	
	    _dft();
	    
	    if(_mode) {
	        index = 0;
	    }
	    else {
	        index = 1;
	    }

            if(_outputMode == 0) {
                for(int i = 1;i < 32;i++) {
	            subChannel[i - 1].send(index, 
		        new DoubleToken(Math.round(_data[2*i])), 1.0);
	            subChannel[i - 1].send(index,
		        new DoubleToken(Math.round(_data[2*i + 1])), 1.0);
	        }
	    }
	    else {
	        double [] out = new double[62];
		for(int i = 2;i < 64;i++) {
		    out[i - 2] = Math.round(_data[i]);
		}
	        subChannel[0].send(index, new ObjectToken(out), 1.0);
	    }
        }
    }       
    
    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
    
    private void _dft() {
        transform_internal(-1);
    }

    private void transform_internal(int direction) {

	int n = 64;
	int logn = 6;

        bitreverse();

        for(int bit = 0, dual = 1; bit < logn; bit++, dual *= 2) {
            double w_real = 1.0;
            double w_imag = 0.0;

            double theta = 2.0 * direction * Math.PI / (2.0 * (double) dual);
            double s = Math.sin(theta);
            double t = Math.sin(theta / 2.0);
            double s2 = 2.0 * t * t;
       
            for (int b = 0; b < n; b += 2 * dual) {
                int i = 2*b;
                int j = 2*(b + dual);

                double wd_real = _data[j] ;
                double wd_imag = _data[j+1] ;
          
                _data[j] = _data[i] - wd_real;
                _data[j+1] = _data[i+1] - wd_imag;
                _data[i] += wd_real;
                _data[i+1] += wd_imag;
            }
      
            for (int a = 1; a < dual; a++) {
                { 
                    double tmp_real = w_real - s * w_imag - s2 * w_real;
                    double tmp_imag = w_imag + s * w_real - s2 * w_imag;
                    w_real = tmp_real;
                    w_imag = tmp_imag;
                }
                
		for(int b = 0; b < n; b += 2 * dual) {
                    int i = 2*(b + a);
                    int j = 2*(b + a + dual);

                    double z1_real = _data[j];
                    double z1_imag = _data[j+1];
              
                    double wd_real = w_real * z1_real - w_imag * z1_imag;
                    double wd_imag = w_real * z1_imag + w_imag * z1_real;

                    _data[j] = _data[i] - wd_real;
                    _data[j+1] = _data[i+1] - wd_imag;
                    _data[i]  += wd_real;
                    _data[i+1]+= wd_imag;
                }
            }
        }
    }

    private void bitreverse() {
	int i = 0; 
	int j = 0;
    
        for(; i < 63; i++) {

            int ii = i << 1;
            int jj = j << 1;
            int k = 64 >> 1;

            if (i < j) {
                double tmp_real = _data[ii];
                double tmp_imag = _data[ii+1];
                _data[ii] = _data[jj];
                _data[ii+1] = _data[jj+1];
                _data[jj] = tmp_real;
                _data[jj+1] = tmp_imag;
	    }
            while(k <= j) {
		j -= k;
                k >>= 1 ; 
	    }
            j += k;
        }
    }

    private void _createInputInterface() throws IllegalActionException,
            NameDuplicationException {        	
	input = new DEIOPort(this, "input", true, false);
	if(_inputMode == 0) {
	    input.setTypeEquals(BaseType.DOUBLE);
	}
	else 
	if(_inputMode == 1) {
	    input.setTypeEquals(BaseType.OBJECT);
	}
	else {
	    throw new IllegalActionException(this, "Invalid input mode");
	}	
    }

    private void _createOutputInterface() throws IllegalActionException, 
            NameDuplicationException {
        if(_outputMode == 0) {
	    subChannel = new DEIOPort[31];
	    for(int i = 0;i < 31;i++) {
	        subChannel[i] = new DEIOPort(this, "subChannel_" + i,
	            false, true);
	        subChannel[i].setTypeEquals(BaseType.DOUBLE);
	        subChannel[i].setMultiport(true);
	    
	        input.delayTo(subChannel[i]);
	        mode.delayTo(subChannel[i]);
	    }
	}
	else
	if(_outputMode == 1) {
	    subChannel = new DEIOPort[1];
	    subChannel[0] = new DEIOPort(this, "subChannel0", false, true);
	    subChannel[0].setTypeEquals(BaseType.OBJECT);
	    subChannel[0].setMultiport(true);
	    input.delayTo(subChannel[0]);
	    mode.delayTo(subChannel[0]);
	}
	else {
            throw new IllegalActionException(this, "Invalid output mode");	
	}                
    }
        
    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////
    
    private double _data[];
    private boolean _mode;
    
    private int _inputMode;
    private int _outputMode;
}
